import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import json
import os
import sys

# 添加项目根目录到路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from main import encode_message, get_versions, get_active_profile
from config_loader import load_frame_config
from protocol.codec import to_hex
from version_manager import get_frame_cfg_path_for_cmd
from gui.json_editor import JsonEditor
from gui.simulator_parser import (
    extract_simulators,
    format_simulator_type_display,
    format_simulator_status,
    get_simulator_statistics
)
from gui.simulator_dialog import SimulatorDialog
from gui.field_selector import FieldSelectorDialog
from gui import simulator_storage


class EncodePanel:
    def __init__(self, parent, versions, active_profile):
        self.parent = parent
        self.versions = versions
        self.active_profile = active_profile
        # 可选的TCP引用（由主窗口注入）
        self._tcp_client = None
        self._tcp_logs = None
        # 指令声明与界面状态缓存
        self._cmd_json_cache = {}   # version -> {cmd_id -> 原始文件内容(str)}
        self._cmd_states = {}       # version -> {cmd_id -> {'freq': str, 'json_editable': bool, 'json_text': str}}
        self._last_cmd_id = None
        self._last_version = None   # 记录上一次的版本
        # 任务钩子在主窗口注入，需先占位，避免初始化早期访问属性报错
        self._task_start_cb = None
        self._task_stop_cb = None
        self._task_is_running_cb = None

        # 模拟器状态相关
        self._simulator_update_timer = None  # 防抖动定时器
        self._simulator_collapsed = False    # 折叠状态
        self._simulator_data = {}            # 存储每行对应的模拟器完整信息 {item_id: simulator_info}
        self._selected_simulator_item = None # 当前选中的模拟器行ID

        # 创建主框架
        self.frame = ttk.LabelFrame(parent, text="", padding=10)
        
        self.setup_ui()
        
    def setup_ui(self):
        """设置编码面板界面"""
        # 版本和指令选择（第一行）
        config_frame = ttk.Frame(self.frame)
        config_frame.pack(fill=tk.X, pady=(0, 6))
        
        # 协议版本
        ttk.Label(config_frame, text="协议版本:").pack(side=tk.LEFT)
        self.version_var = tk.StringVar(value=self.active_profile)
        self.version_combo = ttk.Combobox(config_frame, textvariable=self.version_var,
                                         values=list(self.versions.keys()), state="readonly", width=16)
        self.version_combo.pack(side=tk.LEFT, padx=(5, 20))
        self.version_combo.bind("<<ComboboxSelected>>", self.on_version_changed)
        
        # 指令ID
        ttk.Label(config_frame, text="指令ID:").pack(side=tk.LEFT)
        self.cmd_var = tk.StringVar()
        self.cmd_combo = ttk.Combobox(config_frame, textvariable=self.cmd_var, width=32)
        self.cmd_combo.pack(side=tk.LEFT, padx=(5, 20))
        self.cmd_combo.bind("<<ComboboxSelected>>", self.on_cmd_changed)

        # SIM卡号（第二行）
        sim_frame = ttk.Frame(self.frame)
        sim_frame.pack(fill=tk.X, pady=(0, 10))
        ttk.Label(sim_frame, text="SIM卡号:").pack(side=tk.LEFT)
        self.sim_var = tk.StringVar()
        self.sim_entry = ttk.Entry(sim_frame, textvariable=self.sim_var, width=18)
        self.sim_entry.pack(side=tk.LEFT, padx=(5, 0))
        
        # 参数编辑区域 - 直接使用JSON编辑器，不添加外层标签框
        self.json_editor = JsonEditor(self.frame)
        
        # 操作按钮
        button_frame = ttk.Frame(self.frame)
        button_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.btn_generate = ttk.Button(button_frame, text="生成数据帧", command=self.generate_frame)
        self.btn_generate.pack(side=tk.LEFT)
        # 发送到TCP按钮（如果主窗口集成了tcp_client）
        self.btn_send = ttk.Button(button_frame, text="发送到TCP", command=self.send_to_tcp)
        self.btn_send.pack(side=tk.LEFT, padx=(8, 0))
        # 自动发送频次与按钮
        self.auto_btn = ttk.Button(button_frame, text="自动发送", command=self.toggle_auto_send)
        self.auto_btn.pack(side=tk.LEFT, padx=(8, 0))
        ttk.Label(button_frame, text="频次(s):").pack(side=tk.LEFT, padx=(12, 4))
        self.auto_freq_var = tk.StringVar(value='30')
        self.auto_freq_entry = ttk.Entry(button_frame, textvariable=self.auto_freq_var, width=6)
        self.auto_freq_entry.pack(side=tk.LEFT)

        # 模拟器状态显示区域
        self.simulator_frame = ttk.Frame(self.frame)
        self.simulator_frame.pack(fill=tk.BOTH, pady=(0, 10))

        # 模拟器标题栏（可折叠）
        self.simulator_header_frame = ttk.Frame(self.simulator_frame)
        self.simulator_header_frame.pack(fill=tk.X)

        self.simulator_toggle_btn = ttk.Button(
            self.simulator_header_frame,
            text="▼",
            width=3,
            command=self.toggle_simulator_panel
        )
        self.simulator_toggle_btn.pack(side=tk.LEFT)

        self.simulator_title_var = tk.StringVar(value="模拟器状态 (加载中...)")
        self.simulator_title_label = ttk.Label(
            self.simulator_header_frame,
            textvariable=self.simulator_title_var,
            font=('', 9, 'bold')
        )
        self.simulator_title_label.pack(side=tk.LEFT, padx=(5, 0))

        # 添加模拟器按钮
        self.add_simulator_btn = ttk.Button(
            self.simulator_header_frame,
            text="+添加模拟器",
            command=self.add_simulator
        )
        self.add_simulator_btn.pack(side=tk.RIGHT, padx=(0, 5))

        # 模拟器表格容器（可折叠）
        self.simulator_table_frame = ttk.Frame(self.simulator_frame)
        self.simulator_table_frame.pack(fill=tk.BOTH, expand=False, pady=(5, 0))

        # 创建Treeview表格
        columns = ('type', 'targets', 'config', 'status')
        self.simulator_tree = ttk.Treeview(
            self.simulator_table_frame,
            columns=columns,
            show='headings',
            height=5,  # 固定5行高度
            selectmode='browse'
        )

        # 设置列标题
        self.simulator_tree.heading('type', text='类型')
        self.simulator_tree.heading('targets', text='目标字段')
        self.simulator_tree.heading('config', text='关键配置')
        self.simulator_tree.heading('status', text='状态')

        # 设置列宽度
        self.simulator_tree.column('type', width=60, minwidth=50)
        self.simulator_tree.column('targets', width=200, minwidth=150)
        self.simulator_tree.column('config', width=200, minwidth=150)
        self.simulator_tree.column('status', width=80, minwidth=70)

        # 添加滚动条
        simulator_scrollbar = ttk.Scrollbar(
            self.simulator_table_frame,
            orient=tk.VERTICAL,
            command=self.simulator_tree.yview
        )
        self.simulator_tree.configure(yscrollcommand=simulator_scrollbar.set)

        self.simulator_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        simulator_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 绑定双击事件定位到JSON字段
        self.simulator_tree.bind('<Double-Button-1>', self.on_simulator_double_click)

        # 绑定右键菜单
        self.simulator_tree.bind('<Button-3>', self.on_simulator_right_click)

        # 创建右键菜单
        self.simulator_context_menu = tk.Menu(self.simulator_tree, tearoff=0)
        self.simulator_context_menu.add_command(label="编辑模拟器", command=self.edit_simulator)
        self.simulator_context_menu.add_command(label="删除模拟器", command=self.delete_simulator)
        self.simulator_context_menu.add_separator()
        self.simulator_context_menu.add_command(label="启用模拟器", command=self.enable_simulator)
        self.simulator_context_menu.add_command(label="禁用模拟器", command=self.disable_simulator)
        self.simulator_context_menu.add_separator()
        self.simulator_context_menu.add_command(label="定位目标字段", command=self.locate_target_fields)

        # 结果显示
        result_frame = ttk.LabelFrame(self.frame, text="生成结果", padding=5)
        result_frame.pack(fill=tk.BOTH, expand=True)
        
        self.result_text = scrolledtext.ScrolledText(result_frame, height=10, wrap=tk.WORD, font=('Consolas', 10))
        self.result_text.pack(fill=tk.BOTH, expand=True)
        
        # 加载指令列表并设置默认值（在result_text初始化之后）
        self.load_command_list()
        self.set_default_command()
        self.set_default_sim()
        
        # 初始化版本记录（在第一次加载后设置）
        self._last_version = self.active_profile

        # 自动发送相关回调占位，由主窗口注入
        self._task_start_cb = None
        self._task_stop_cb = None
        self._task_is_running_cb = None

        # 设置JSON编辑监听（防抖动更新模拟器）
        self.setup_json_monitor()

        # 初始更新模拟器显示
        self.frame.after(500, self.update_simulator_display)

    def get_cached_content(self, version: str, cmd_id: str) -> str:
        """获取缓存的指令内容"""
        return self._cmd_json_cache.get(version, {}).get(cmd_id, '')

    def set_cached_content(self, version: str, cmd_id: str, content: str):
        """设置缓存的指令内容"""
        if version not in self._cmd_json_cache:
            self._cmd_json_cache[version] = {}
        self._cmd_json_cache[version][cmd_id] = content

    def _filter_simulators_field(self, json_text: str) -> str:
        """
        从JSON文本中过滤掉simulators字段

        Args:
            json_text: 原始JSON文本

        Returns:
            过滤后的JSON文本
        """
        try:
            import json as _json
            obj = _json.loads(json_text)
            if isinstance(obj, dict) and 'simulators' in obj:
                # 创建副本并移除simulators字段
                filtered_obj = {k: v for k, v in obj.items() if k != 'simulators'}
                return _json.dumps(filtered_obj, indent=2, ensure_ascii=False)
            return json_text
        except Exception:
            # 如果解析失败，返回原始文本
            return json_text
        
    def load_command_list(self):
        """加载指令列表，显示指令ID和name"""
        # 从版本配置中获取支持的指令
        version_config = self.versions.get(self.active_profile, {})
        commands = version_config.get('cmds', [])
        
        # 如果没有从版本配置获取到指令，尝试从目录扫描
        if not commands:
            frames_dir = version_config.get('path', '')
            if frames_dir and os.path.exists(frames_dir):
                for file in os.listdir(frames_dir):
                    if file.endswith('.json'):
                        cmd_id = file.replace('.json', '')
                        commands.append(cmd_id)
        
        # 为每个指令加载配置，获取name信息，并缓存原始文件
        command_display_list = []
        # 使用当前活动的版本标识
        current_version = self.active_profile
        for cmd_id in sorted(commands):
            try:
                # 加载指令配置
                frame_cfg_path, _ = get_frame_cfg_path_for_cmd(cmd_id, current_version)
                frame_cfg = load_frame_config(frame_cfg_path)
                # 检查是否已缓存原始文件内容
                if not self.get_cached_content(current_version, cmd_id):
                    # 优先缓存被引用的原始文件内容（若当前是ref stub）
                    try:
                        import json as _json, os as _os
                        with open(frame_cfg_path, 'r', encoding='utf-8') as f:
                            raw_obj = _json.load(f)
                        if isinstance(raw_obj, dict) and 'ref' in raw_obj and isinstance(raw_obj['ref'], dict):
                            ref = raw_obj['ref']
                            # 解析 frames 根路径
                            current_dir = _os.path.dirname(frame_cfg_path)
                            frames_root = current_dir
                            while _os.path.basename(frames_root) != 'frames' and frames_root != _os.path.dirname(frames_root):
                                frames_root = _os.path.dirname(frames_root)
                            if _os.path.basename(frames_root) != 'frames':
                                frames_root = _os.path.dirname(current_dir)
                            target_path = _os.path.join(frames_root, ref.get('standard',''), ref.get('edition',''), f"{str(ref.get('cmd','')).lower()}.json")
                            if _os.path.exists(target_path):
                                with open(target_path, 'r', encoding='utf-8') as tf:
                                    content = tf.read()
                                    # 过滤simulators字段
                                    filtered_content = self._filter_simulators_field(content)
                                    self.set_cached_content(current_version, cmd_id, filtered_content)
                            else:
                                # 回退缓存为解析后的配置
                                content = _json.dumps(frame_cfg, indent=2, ensure_ascii=False)
                                filtered_content = self._filter_simulators_field(content)
                                self.set_cached_content(current_version, cmd_id, filtered_content)
                        else:
                            # 非ref，直接缓存当前文件文本
                            with open(frame_cfg_path, 'r', encoding='utf-8') as rf:
                                content = rf.read()
                                # 过滤simulators字段
                                filtered_content = self._filter_simulators_field(content)
                                self.set_cached_content(current_version, cmd_id, filtered_content)
                    except Exception:
                        import json as _json
                        content = _json.dumps(frame_cfg, indent=2, ensure_ascii=False)
                        filtered_content = self._filter_simulators_field(content)
                        self.set_cached_content(current_version, cmd_id, filtered_content)
                # 初始化该版本下该指令的状态（如果还未初始化）
                if current_version not in self._cmd_states:
                    self._cmd_states[current_version] = {}
                if cmd_id not in self._cmd_states[current_version]:
                    self._cmd_states[current_version][cmd_id] = {'freq': '30', 'json_editable': True, 'json_text': self.get_cached_content(current_version, cmd_id)}
                
                # 获取指令名称
                cmd_name = frame_cfg.get('name', '')
                if cmd_name:
                    display_text = f"{cmd_id} - {cmd_name}"
                else:
                    display_text = cmd_id
                    
                command_display_list.append(display_text)
            except Exception:
                # 如果加载失败，只显示指令ID
                command_display_list.append(cmd_id)
        
        self.cmd_combo['values'] = command_display_list
        
    def set_default_command(self):
        """设置默认指令，优先选择0x0200，不存在则选择最小的指令"""
        commands = self.cmd_combo['values']
        if not commands:
            return
            
        # 优先选择0x0200
        for cmd_display in commands:
            if cmd_display.startswith('0x0200'):
                self.cmd_var.set(cmd_display)
                break
        else:
            # 如果0x0200不存在，选择第一个指令
            self.cmd_var.set(commands[0])
        
        # 触发指令改变事件，加载对应的配置
        self.on_cmd_changed()
        
    def set_default_sim(self):
        """设置默认SIM卡号"""
        from state_io import load_state, save_state
        st = load_state()
        sim = st.get('sim', '13800138000')
        self.sim_var.set(sim)
        # 监听变化并写入state
        def on_change(*_):
            s = self.sim_var.get().strip()
            st = load_state()
            st['sim'] = s
            save_state(st)
        self.sim_var.trace_add('write', lambda *_: on_change())
        
    def clear_params(self):
        """清空参数输入框"""
        if hasattr(self, 'json_editor'):
            self.json_editor.clear()
        
    def on_version_changed(self, event=None):
        """版本改变时的处理"""
        # 获取新旧版本
        old_version = self.active_profile
        new_version = self.version_var.get()
        
        # 如果版本没有变化，直接返回
        if old_version == new_version:
            return
        
        # 记录当前选中的指令ID（用于版本切换后保持选中）
        old_cmd_id = None
        current_cmd_display = self.cmd_var.get()
        if current_cmd_display:
            old_cmd_id = current_cmd_display.split(' - ')[0] if ' - ' in current_cmd_display else current_cmd_display
        
        # 在切换版本前，先保存当前显示的指令编辑内容
        if current_cmd_display and old_version and hasattr(self, 'json_editor') and hasattr(self.json_editor, 'json_text'):
            try:
                # 确保旧版本的状态字典存在
                if old_version not in self._cmd_states:
                    self._cmd_states[old_version] = {}
                if old_cmd_id not in self._cmd_states[old_version]:
                    self._cmd_states[old_version][old_cmd_id] = {'freq': '30', 'json_editable': True, 'json_text': ''}
                
                # 保存当前编辑的内容
                current_content = self.json_editor.json_text.get('1.0', tk.END)
                self._cmd_states[old_version][old_cmd_id]['json_text'] = current_content
                self._cmd_states[old_version][old_cmd_id]['freq'] = self.auto_freq_var.get()
                self._cmd_states[old_version][old_cmd_id]['json_editable'] = (str(self.json_editor.json_text.cget('state')) == str(tk.NORMAL))
            except Exception:
                pass
        
        # 更新为新版本
        self.active_profile = new_version
        
        # 重置最后指令记录（避免 on_cmd_changed 重复保存）
        self._last_cmd_id = None
        self._last_version = new_version
        
        # 加载新版本的指令列表
        self.load_command_list()
        
        # 尝试保持当前指令ID不变（如果新版本中存在）
        new_cmd_set = False
        if old_cmd_id:
            # 检查新版本的指令列表中是否存在该指令
            commands = self.cmd_combo['values']
            for cmd_display in commands:
                cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
                if cmd_id == old_cmd_id:
                    # 找到相同的指令，保持选中
                    self.cmd_var.set(cmd_display)
                    new_cmd_set = True
                    break
        
        # 如果没有设置指令（新版本中不存在旧指令），则设置默认指令
        if not new_cmd_set:
            self.set_default_command()
        else:
            # 手动触发指令改变事件，加载该指令的内容
            self.on_cmd_changed()
        
        # 清空输出结果
        self.result_text.delete(1.0, tk.END)

        # 版本切换后立即更新模拟器显示
        self.frame.after(100, self.update_simulator_display)

    def on_cmd_changed(self, event=None):
        """指令改变时的处理"""
        cmd_display = self.cmd_var.get()
        if not cmd_display:
            self.json_editor.clear()
            return
            
        # 先保存上一个指令的状态（如果存在且版本有效）
        if self._last_cmd_id and self._last_version and hasattr(self.json_editor, 'json_text'):
            try:
                # 确保上一个版本的状态字典存在
                if self._last_version in self._cmd_states and self._last_cmd_id in self._cmd_states[self._last_version]:
                    # 获取当前编辑的内容
                    current_content = self.json_editor.json_text.get('1.0', tk.END)
                    # 更新界面状态缓存（保存到对应版本下）
                    self._cmd_states[self._last_version][self._last_cmd_id]['json_text'] = current_content
                    self._cmd_states[self._last_version][self._last_cmd_id]['freq'] = self.auto_freq_var.get()
                    self._cmd_states[self._last_version][self._last_cmd_id]['json_editable'] = (str(self.json_editor.json_text.cget('state')) == str(tk.NORMAL))
            except Exception:
                pass
        
        # 当前指令ID和版本
        cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
        current_version = self.active_profile
        
        # 确保当前版本的状态字典存在
        if current_version not in self._cmd_states:
            self._cmd_states[current_version] = {}
        
        # 获取当前版本下该指令的状态
        st = self._cmd_states[current_version].get(cmd_id)
        if not st:
            # 如果该指令在当前版本下没有状态缓存，从原始文件缓存中加载内容并初始化状态
            cached_content = self.get_cached_content(current_version, cmd_id)
            if cached_content:
                # 初始化该指令在当前版本下的状态
                self._cmd_states[current_version][cmd_id] = {
                    'freq': '30', 
                    'json_editable': True, 
                    'json_text': cached_content
                }
                st = self._cmd_states[current_version][cmd_id]
            else:
                # 如果原始文件缓存中也没有内容，清空界面
                self.json_editor.clear()
                self.result_text.delete(1.0, tk.END)
                self._last_cmd_id = cmd_id
                self._last_version = current_version
                return
        # 渲染缓存内容
        try:
            self.json_editor.clear_widgets()
            self.json_editor.create_json_form({}, None)
            self.json_editor.json_text.delete('1.0', tk.END)
            self.json_editor.json_text.insert('1.0', st.get('json_text', ''))
        except Exception:
            self.json_editor.clear()
        # 控件状态
        self.auto_freq_var.set(st.get('freq', '30'))
        running = False
        try:
            cb = getattr(self, '_task_is_running_cb', None)
            running = bool(cb(cmd_id)) if cb else False
        except Exception:
            running = False
        self.auto_btn.config(text=('停止自动发送' if running else '自动发送'))
        self.auto_freq_entry.config(state=(tk.DISABLED if running else tk.NORMAL))
        
        # 恢复该指令的编辑状态（考虑自动发送状态和指令本身的编辑状态）
        json_editable = st.get('json_editable', True) and not running
        self.json_editor.set_editable(json_editable)
        
        # 生成/发送按钮在自动发送启用时禁用
        try:
            self.btn_generate.config(state=(tk.DISABLED if running else tk.NORMAL))
            self.btn_send.config(state=(tk.DISABLED if running else tk.NORMAL))
        except Exception:
            pass
        # 清空生成结果
        self.result_text.delete(1.0, tk.END)
        # 更新记录的当前指令和版本
        self._last_cmd_id = cmd_id
        self._last_version = current_version

        # 指令切换后更新模拟器显示
        self.frame.after(100, self.update_simulator_display)

    def check_cmd_consistency(self):
        """检查当前选择的指令ID与JSON中的cmd字段是否一致"""
        cmd_display = self.cmd_var.get()
        if not cmd_display:
            return True, None
            
        # 从显示文本中提取指令ID
        selected_cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
        
        try:
            # 获取当前JSON内容
            json_content = self.json_editor.get_values()
            if isinstance(json_content, dict) and 'cmd' in json_content:
                json_cmd = json_content['cmd']
                # 标准化比较（去除大小写差异和0x前缀）
                selected_normalized = selected_cmd_id.lower().replace('0x', '')
                json_normalized = str(json_cmd).lower().replace('0x', '')
                
                # 严格比较：必须完全一致
                if selected_normalized != json_normalized:
                    return False, (selected_cmd_id, json_cmd)
            else:
                # JSON中没有cmd字段，认为不一致
                return False, (selected_cmd_id, "JSON中缺少cmd字段")
        except Exception as e:
            # 如果解析失败，认为不一致
            return False, (selected_cmd_id, f"JSON解析失败: {str(e)}")
            
        return True, None

    def generate_frame(self):
        """生成数据帧"""
        cmd_display = self.cmd_var.get()
        if not cmd_display:
            messagebox.showwarning("警告", "请选择指令ID")
            return
            
        # 检查指令ID一致性
        is_consistent, mismatch_info = self.check_cmd_consistency()
        if not is_consistent:
            selected_cmd, json_cmd = mismatch_info
            messagebox.showwarning(
                "指令ID不一致", 
                f"当前选择的指令ID: {selected_cmd}\n"
                f"JSON中的cmd字段: {json_cmd}\n\n"
                f"请确保两者一致后再进行操作。"
            )
            return
            
        # 从显示文本中提取指令ID
        cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
            
        try:
            # 获取当前参数
            params = self.json_editor.get_values()
            
            # 获取SIM卡号
            sim_number = self.sim_var.get().strip()
            if not sim_number:
                sim_number = '13800138000'  # 使用默认值
            
            # 加载解析后的配置（用于编码）
            frame_cfg_path, _ = get_frame_cfg_path_for_cmd(cmd_id, self.active_profile)
            frame_cfg = load_frame_config(frame_cfg_path)
            
            # 使用main.py中的函数生成数据帧，传入JSON配置和SIM卡号
            frame_bytes = encode_message(cmd_id, self.active_profile, params, sim_number, sim=sim_number)
            hex_result = to_hex(frame_bytes)
            
            # 仅显示数据帧
            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, hex_result)
            # 缓存最近一次生成，用于发送
            self._last_frame_bytes = frame_bytes
            
        except json.JSONDecodeError as e:
            messagebox.showerror("JSON格式错误", f"参数输入框不是合法的JSON格式:\n{str(e)}\n\n请检查JSON语法是否正确。")
        except ValueError as e:
            if "不是合法的十六进制" in str(e):
                messagebox.showerror("十六进制格式错误", f"输入的数据不是合法的十六进制格式:\n{str(e)}\n\n请确保输入的是有效的十六进制字符串。")
            else:
                messagebox.showerror("参数错误", f"参数验证失败: {str(e)}")
        except Exception as e:
            messagebox.showerror("生成数据帧失败", f"生成数据帧时发生错误: {str(e)}")
            
        
    def send_to_tcp(self):
        # 检查指令ID一致性
        is_consistent, mismatch_info = self.check_cmd_consistency()
        if not is_consistent:
            selected_cmd, json_cmd = mismatch_info
            messagebox.showwarning(
                "指令ID不一致", 
                f"当前选择的指令ID: {selected_cmd}\n"
                f"JSON中的cmd字段: {json_cmd}\n\n"
                f"请确保两者一致后再进行操作。"
            )
            return
            
        # 每次点击都重新生成数据帧
        try:
            self.generate_frame()
            # 检查生成结果
            result_content = self.result_text.get(1.0, tk.END).strip()
            if not result_content:
                messagebox.showwarning('提示', '无法生成数据帧，请检查参数')
                return
        except Exception as e:
            messagebox.showerror('生成失败', f'生成数据帧失败: {str(e)}')
            return
        
        # 获取新生成的字节数据
        b = getattr(self, '_last_frame_bytes', None)
        if not b:
            messagebox.showwarning('提示', '没有可发送的数据帧')
            return
            
        # 使用注入的TCP引用
        tcp_client = self._tcp_client
        tcp_logs = self._tcp_logs
        if not tcp_client:
            messagebox.showerror('错误', '未找到TCP客户端，请在左侧先连接')
            return
        # 检查连接状态
        try:
            connected = bool(getattr(tcp_client, 'connected', False))
        except Exception:
            connected = False
        if not connected:
            messagebox.showwarning('提示', 'TCP未连接，请先连接服务器')
            return
        try:
            tcp_client.send(b)
            # 记录发送日志
            if tcp_logs:
                cmd_display = self.cmd_var.get()
                # 使用完整的显示格式（指令ID-指令名称）
                tcp_logs.append_send(cmd_display, b)
            # 不弹窗
        except Exception as e:
            messagebox.showerror('发送失败', str(e))

    def set_tcp_refs(self, client, logs):
        self._tcp_client = client
        self._tcp_logs = logs

    def set_task_hooks(self, start_cb, stop_cb, is_running_cb):
        self._task_start_cb = start_cb
        self._task_stop_cb = stop_cb
        self._task_is_running_cb = is_running_cb

    def _current_cmd_id(self) -> str:
        cmd_display = self.cmd_var.get()
        return cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display

    def _current_cmd_display(self) -> str:
        return self.cmd_var.get() or self._current_cmd_id()

    def _set_json_editable(self, enabled: bool):
        try:
            self.json_editor.set_editable(enabled)
        except Exception:
            pass

    def toggle_auto_send(self):
        """启用/停止当前指令的自动发送。"""
        if not (self._task_start_cb and self._task_stop_cb and self._task_is_running_cb):
            messagebox.showerror('错误', '自动发送未正确初始化')
            return
        cmd_id = self._current_cmd_id()
        display = self._current_cmd_display().replace(' - ', '-')
        running = self._task_is_running_cb(cmd_id)
        if running:
            # 停止
            self._task_stop_cb(cmd_id)
            self.auto_btn.config(text='自动发送')
            self.auto_freq_entry.config(state=tk.NORMAL)
            self._set_json_editable(True)
            try:
                self.btn_generate.config(state=tk.NORMAL)
                self.btn_send.config(state=tk.NORMAL)
            except Exception:
                pass
            return
        # 启动前检查指令ID一致性
        is_consistent, mismatch_info = self.check_cmd_consistency()
        if not is_consistent:
            selected_cmd, json_cmd = mismatch_info
            messagebox.showwarning(
                "指令ID不一致", 
                f"当前选择的指令ID: {selected_cmd}\n"
                f"JSON中的cmd字段: {json_cmd}\n\n"
                f"请确保两者一致后再进行操作。"
            )
            return
        # 启动
        try:
            freq = int(self.auto_freq_var.get().strip() or '30')
            if freq <= 0:
                raise ValueError('频次需为正整数')
        except Exception:
            messagebox.showerror('错误', '频次需为正整数')
            return
        # 缓存当前JSON文本作为任务的固定参数
        try:
            cached_json_str = self.json_editor.json_text.get('1.0', tk.END)
        except Exception:
            cached_json_str = None

        def json_disable_setter(disabled: bool):
            # 仅禁用编辑器与频次输入，指令允许切换
            self._set_json_editable(not disabled)
            self.auto_freq_entry.config(state=tk.DISABLED if disabled else tk.NORMAL)
            try:
                # 同步生成/发送按钮禁用状态
                self.btn_generate.config(state=(tk.DISABLED if disabled else tk.NORMAL))
                self.btn_send.config(state=(tk.DISABLED if disabled else tk.NORMAL))
            except Exception:
                pass

        def generate_and_send():
            # 使用缓存JSON，不受后来编辑影响
            # 解析缓存JSON为对象
            params = None
            if cached_json_str is not None:
                import json as _json
                try:
                    params = _json.loads(cached_json_str)
                except Exception as e:
                    raise ValueError(f'自动发送时JSON解析失败: {e}')
            # 生成与发送
            sim_number = (self.sim_var.get() or '').strip() or '13800138000'
            frame_bytes = encode_message(cmd_id, self.active_profile, params, sim_number, sim=sim_number)
            if self._tcp_logs:
                self._tcp_logs.append_send(display, frame_bytes)
            if self._tcp_client and getattr(self._tcp_client, 'connected', False):
                self._tcp_client.send(frame_bytes)
                return True
            else:
                raise ValueError('TCP未连接')

        ok = self._task_start_cb(cmd_id, display, freq, json_disable_setter, generate_and_send)
        if ok:
            self.auto_btn.config(text='停止自动发送')
            self.auto_freq_entry.config(state=tk.DISABLED)
            self._set_json_editable(False)
            try:
                self.btn_generate.config(state=tk.DISABLED)
                self.btn_send.config(state=tk.DISABLED)
            except Exception:
                pass

    # ===== 模拟器状态显示相关方法 =====

    def toggle_simulator_panel(self):
        """切换模拟器面板折叠/展开状态"""
        self._simulator_collapsed = not self._simulator_collapsed

        if self._simulator_collapsed:
            # 折叠：隐藏表格
            self.simulator_table_frame.pack_forget()
            self.simulator_toggle_btn.config(text="▶")
        else:
            # 展开：显示表格
            self.simulator_table_frame.pack(fill=tk.BOTH, expand=False, pady=(5, 0))
            self.simulator_toggle_btn.config(text="▼")

    def update_simulator_display(self, force_update=False):
        """
        更新模拟器状态显示

        Args:
            force_update: 是否强制更新（忽略防抖动）
        """
        try:
            # 获取当前版本和指令ID
            cmd_display = self.cmd_var.get()
            if not cmd_display:
                self._show_no_simulator_message("未选择指令")
                return

            cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
            profile = self.active_profile

            # 从独立存储提取模拟器信息
            simulators = extract_simulators(profile, cmd_id)

            # 更新显示
            if not simulators:
                self._show_no_simulator_message("当前指令没有配置模拟器")
            else:
                self._display_simulators(simulators)

        except Exception as e:
            self._show_no_simulator_message(f"解析失败: {str(e)}")

    def _display_simulators(self, simulators: list):
        """显示模拟器列表"""
        # 清空现有内容和数据
        for item in self.simulator_tree.get_children():
            self.simulator_tree.delete(item)
        self._simulator_data.clear()

        # 更新标题
        stats = get_simulator_statistics(simulators)
        title = f"模拟器状态 (共{stats['total']}个，已启用{stats['enabled']}个)"
        self.simulator_title_var.set(title)

        # 添加数据行
        for sim in simulators:
            # 判断是否为 location 的第二行（子行）
            is_first_row = sim.get('is_first_row', True)

            # 类型列：如果是子行，显示缩进符号
            if not is_first_row:
                type_display = "  └─ "
            else:
                type_display = format_simulator_type_display(sim['type'])

            values = (
                type_display,  # 使用处理过的类型显示
                sim['target_display'],
                sim['config_summary'],
                format_simulator_status(sim['enabled'])
            )
            item_id = self.simulator_tree.insert('', tk.END, values=values)
            # 保存完整的模拟器信息
            self._simulator_data[item_id] = sim

    def _show_no_simulator_message(self, message: str):
        """显示无模拟器提示信息"""
        # 清空现有内容
        for item in self.simulator_tree.get_children():
            self.simulator_tree.delete(item)

        # 更新标题
        self.simulator_title_var.set(f"模拟器状态 ({message})")

        # 显示提示行
        self.simulator_tree.insert('', tk.END, values=('', '', message, ''))

    def schedule_simulator_update(self, delay_ms=0):
        """
        调度模拟器更新（支持立即更新或延迟更新）

        Args:
            delay_ms: 延迟毫秒数，默认0表示立即更新
        """
        # 取消之前的定时器
        if self._simulator_update_timer:
            try:
                self.frame.after_cancel(self._simulator_update_timer)
            except Exception:
                pass

        if delay_ms == 0:
            # 立即更新
            self.update_simulator_display()
        else:
            # 延迟更新
            self._simulator_update_timer = self.frame.after(delay_ms, self.update_simulator_display)

    def setup_json_monitor(self):
        """设置JSON编辑监听（立即更新）"""
        def on_json_changed(event=None):
            self.schedule_simulator_update(0)  # 立即更新

        # 延迟绑定，确保json_text已创建
        def bind_monitor():
            if hasattr(self.json_editor, 'json_text') and self.json_editor.json_text:
                try:
                    # 绑定多种编辑事件
                    self.json_editor.json_text.bind('<KeyRelease>', on_json_changed)
                    self.json_editor.json_text.bind('<<Paste>>', on_json_changed)
                    self.json_editor.json_text.bind('<<Cut>>', on_json_changed)
                    self.json_editor.json_text.bind('<<Undo>>', on_json_changed)
                    self.json_editor.json_text.bind('<<Redo>>', on_json_changed)
                except Exception:
                    pass

        # 延迟100ms后绑定，确保json_text已创建
        self.frame.after(100, bind_monitor)

    # ===== 模拟器状态切换相关方法 =====

    def on_simulator_double_click(self, event):
        """双击模拟器行时定位到JSON字段位置"""
        # 获取点击的行
        item_id = self.simulator_tree.identify_row(event.y)
        if not item_id or item_id not in self._simulator_data:
            return

        # 定位到JSON字段
        self.locate_simulator_in_json_by_item(item_id)

    def on_simulator_right_click(self, event):
        """右键点击模拟器行时显示菜单"""
        # 获取点击的行
        item_id = self.simulator_tree.identify_row(event.y)
        if not item_id or item_id not in self._simulator_data:
            return

        # 选中该行
        self.simulator_tree.selection_set(item_id)
        self._selected_simulator_item = item_id

        # 获取模拟器信息，更新菜单项状态
        simulator_info = self._simulator_data[item_id]
        is_enabled = simulator_info['enabled']

        # 更新菜单项状态（新的菜单索引：0=编辑，1=删除，3=启用，4=禁用）
        if is_enabled:
            self.simulator_context_menu.entryconfig(3, state=tk.DISABLED)  # 启用模拟器
            self.simulator_context_menu.entryconfig(4, state=tk.NORMAL)    # 禁用模拟器
        else:
            self.simulator_context_menu.entryconfig(3, state=tk.NORMAL)    # 启用模拟器
            self.simulator_context_menu.entryconfig(4, state=tk.DISABLED)  # 禁用模拟器

        # 显示菜单
        try:
            self.simulator_context_menu.tk_popup(event.x_root, event.y_root)
        finally:
            self.simulator_context_menu.grab_release()

    def enable_simulator(self):
        """启用选中的模拟器"""
        if not self._selected_simulator_item:
            return

        simulator_info = self._simulator_data.get(self._selected_simulator_item)
        if not simulator_info:
            return

        # 获取当前版本和指令ID
        cmd_display = self.cmd_var.get()
        cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
        profile = self.active_profile

        # 更新独立存储
        success = simulator_storage.update_simulator_enable(profile, cmd_id, simulator_info['simulator_index'], True)
        if success:
            # 刷新显示
            self.schedule_simulator_update(0)

    def disable_simulator(self):
        """禁用选中的模拟器"""
        if not self._selected_simulator_item:
            return

        simulator_info = self._simulator_data.get(self._selected_simulator_item)
        if not simulator_info:
            return

        # 获取当前版本和指令ID
        cmd_display = self.cmd_var.get()
        cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
        profile = self.active_profile

        # 更新独立存储
        success = simulator_storage.update_simulator_enable(profile, cmd_id, simulator_info['simulator_index'], False)
        if success:
            # 刷新显示
            self.schedule_simulator_update(0)

    def locate_simulator_in_json(self):
        """从右键菜单中定位到JSON字段"""
        if not self._selected_simulator_item:
            return
        self.locate_simulator_in_json_by_item(self._selected_simulator_item)

    def locate_simulator_in_json_by_item(self, item_id: str):
        """定位到JSON编辑器中的目标字段位置"""
        simulator_info = self._simulator_data.get(item_id)
        if not simulator_info:
            return

        # 获取目标字段
        # 新数据结构：target_field（单字段）或 target_fields（多字段列表）
        target_field = simulator_info.get('target_field')
        target_fields = simulator_info.get('target_fields', [])

        # 确定要定位的字段
        if target_field:
            # 单字段（包括 location 的单个 lat 或 lng）
            field_to_locate = target_field
        elif target_fields:
            # 多字段：定位到第一个
            field_to_locate = target_fields[0]
        else:
            messagebox.showwarning('提示', '无法确定目标字段')
            return

        # 从字段路径中提取字段名（如 "body.time" -> "time"）
        field_name = field_to_locate.split('.')[-1] if '.' in field_to_locate else field_to_locate

        # 检查json_text是否存在
        if not hasattr(self.json_editor, 'json_text') or not self.json_editor.json_text:
            messagebox.showwarning('提示', 'JSON编辑器未初始化')
            return

        json_text_widget = self.json_editor.json_text

        # 在JSON文本中搜索字段
        json_content = json_text_widget.get('1.0', tk.END)

        import re

        # 搜索 "name": "field_name" 定位字段对象
        name_pattern = r'"name"\s*:\s*"' + re.escape(field_name) + r'"'
        name_match = re.search(name_pattern, json_content)

        if not name_match:
            messagebox.showwarning('提示', f'未在JSON中找到字段 "{field_name}"')
            return

        # 定位到 "name" 的起始位置
        name_pos = name_match.start()

        # 转换字节位置为行列位置
        lines_before = json_content[:name_pos].count('\n')
        line_start = json_content.rfind('\n', 0, name_pos) + 1
        col = name_pos - line_start

        # Tkinter的Text控件使用 "line.column" 格式
        tk_pos = f"{lines_before + 1}.{col}"

        # 计算高亮结束位置（整个 "name": "field_name" 部分）
        highlight_end_pos = name_match.end()
        highlight_end_lines = json_content[:highlight_end_pos].count('\n')
        highlight_end_line_start = json_content.rfind('\n', 0, highlight_end_pos) + 1
        highlight_end_col = highlight_end_pos - highlight_end_line_start
        tk_end_pos = f"{highlight_end_lines + 1}.{highlight_end_col}"

        # 定位并高亮
        json_text_widget.see(tk_pos)
        json_text_widget.tag_remove('highlight', '1.0', tk.END)
        json_text_widget.tag_add('highlight', tk_pos, tk_end_pos)

        # 配置高亮样式
        try:
            json_text_widget.tag_config('highlight', background='yellow', foreground='black')
        except Exception:
            pass

        # 设置光标位置
        json_text_widget.mark_set(tk.INSERT, tk_pos)
        json_text_widget.focus_set()

        # 2秒后取消高亮
        self.frame.after(2000, lambda: json_text_widget.tag_remove('highlight', '1.0', tk.END))

    def _update_simulator_enable_in_json(self, field_path: str, new_enabled: bool) -> bool:
        """
        在JSON中更新指定模拟器的enable状态，保持原有格式

        Args:
            field_path: 字段路径，如 "body.time"
            new_enabled: 新的启用状态

        Returns:
            是否更新成功
        """
        try:
            # 检查json_text是否存在
            if not hasattr(self.json_editor, 'json_text') or not self.json_editor.json_text:
                messagebox.showerror('错误', 'JSON编辑器未初始化')
                return False

            # 获取当前JSON文本
            json_text = self.json_editor.json_text.get('1.0', tk.END)

            # 验证JSON格式
            try:
                json.loads(json_text)
            except json.JSONDecodeError as e:
                messagebox.showerror('JSON格式错误', f'无法更新模拟器状态\n\nJSON格式错误: {str(e)}')
                return False

            # 使用正则表达式查找并替换enable值
            # 构建字段路径的正则模式
            updated_text = self._replace_simulator_enable(json_text, field_path, new_enabled)

            if updated_text == json_text:
                # 没有找到匹配的字段
                messagebox.showwarning('警告', f'未找到字段 "{field_path}" 的模拟器配置')
                return False

            # 更新JSON文本
            self.json_editor.json_text.delete('1.0', tk.END)
            self.json_editor.json_text.insert('1.0', updated_text)

            return True

        except Exception as e:
            messagebox.showerror('更新失败', f'更新模拟器状态时出错:\n{str(e)}')
            return False

    def _replace_simulator_enable(self, json_text: str, field_path: str, new_enabled: bool) -> str:
        """
        使用智能匹配替换指定字段的simulator.enable值

        Args:
            json_text: 原始JSON文本
            field_path: 字段路径（如 "body.time"）
            new_enabled: 新的enable值

        Returns:
            更新后的JSON文本
        """
        import re

        # 从路径中提取字段名（最后一个点后的部分）
        if '.' in field_path:
            field_name = field_path.split('.')[-1]
        else:
            field_name = field_path

        # 移除数组索引（如果有）
        field_name_clean = re.sub(r'\[.*?\]', '', field_name)

        # 搜索策略：
        # 1. 先找到 "name": "field_name" 定位字段对象
        # 2. 在该字段对象范围内找到 "simulator" 对象
        # 3. 在 simulator 对象内找到 "enable": true/false 并替换

        # 步骤1：找到name字段
        name_pattern = r'"name"\s*:\s*"' + re.escape(field_name_clean) + r'"'
        name_match = re.search(name_pattern, json_text)

        if not name_match:
            return json_text  # 未找到字段，返回原文本

        # 步骤2：限定搜索范围（从name位置到下一个name或文件结束）
        start_pos = name_match.end()

        # 找到下一个字段的开始位置
        next_field_match = re.search(r'"name"\s*:\s*"', json_text[start_pos:])
        if next_field_match:
            end_pos = start_pos + next_field_match.start()
        else:
            end_pos = len(json_text)

        # 在限定范围内搜索simulator.enable
        # 使用[\s\S]支持跨行匹配
        range_text = json_text[start_pos:end_pos]

        # 步骤3：在范围内找到simulator.enable并替换
        simulator_pattern = (
            r'("simulator"\s*:\s*\{[\s\S]*?'  # simulator对象开始
            r'"enable"\s*:\s*)(true|false)'    # enable字段及其值
        )

        new_value_str = 'true' if new_enabled else 'false'

        # 在范围内执行替换
        updated_range, count = re.subn(simulator_pattern, r'\g<1>' + new_value_str, range_text, count=1)

        if count > 0:
            # 替换成功，重新组装文本
            updated_text = json_text[:start_pos] + updated_range + json_text[end_pos:]
            return updated_text
        else:
            # 未找到simulator.enable
            return json_text



    # ===== 新的模拟器操作方法 =====

    def add_simulator(self):
        """添加新模拟器"""
        try:
            # 获取当前JSON配置（用于字段选择）
            json_config = self.json_editor.get_values()
        except Exception as e:
            messagebox.showerror("错误", f"无法读取JSON配置: {str(e)}")
            return

        # 显示对话框
        dialog = SimulatorDialog(self.frame, json_config)
        result = dialog.show()

        if result:
            # 获取当前版本和指令ID
            cmd_display = self.cmd_var.get()
            cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
            profile = self.active_profile

            # 添加到独立存储
            simulator_storage.add_simulator(profile, cmd_id, result)

            # 刷新显示
            self.schedule_simulator_update(0)

    def edit_simulator(self):
        """编辑选中的模拟器"""
        if not self._selected_simulator_item:
            return

        simulator_info = self._simulator_data.get(self._selected_simulator_item)
        if not simulator_info:
            return

        try:
            # 获取当前JSON配置（用于字段选择）
            json_config = self.json_editor.get_values()
        except Exception as e:
            messagebox.showerror("错误", f"无法读取JSON配置: {str(e)}")
            return

        # 显示编辑对话框
        dialog = SimulatorDialog(self.frame, json_config, simulator_info['full_config'])
        result = dialog.show()

        if result:
            # 获取当前版本和指令ID
            cmd_display = self.cmd_var.get()
            cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
            profile = self.active_profile

            # 更新独立存储中的模拟器
            simulator_storage.update_simulator(profile, cmd_id, simulator_info['simulator_index'], result)

            # 刷新显示
            self.schedule_simulator_update(0)

    def delete_simulator(self):
        """删除选中的模拟器"""
        if not self._selected_simulator_item:
            return

        simulator_info = self._simulator_data.get(self._selected_simulator_item)
        if not simulator_info:
            return

        # 确认删除
        type_display = format_simulator_type_display(simulator_info['type'])
        if not messagebox.askyesno("确认删除", f"确定要删除该{type_display}模拟器吗？"):
            return

        # 获取当前版本和指令ID
        cmd_display = self.cmd_var.get()
        cmd_id = cmd_display.split(' - ')[0] if ' - ' in cmd_display else cmd_display
        profile = self.active_profile

        # 从独立存储删除
        simulator_storage.delete_simulator(profile, cmd_id, simulator_info['simulator_index'])

        # 刷新显示
        self.schedule_simulator_update(0)

    def locate_target_fields(self):
        """定位选中模拟器的所有目标字段"""
        if not self._selected_simulator_item:
            return

        simulator_info = self._simulator_data.get(self._selected_simulator_item)
        if not simulator_info:
            return

        targets_list = simulator_info.get('targets_list', [])
        if not targets_list:
            messagebox.showinfo("提示", "该模拟器没有目标字段")
            return

        # 弹出子菜单让用户选择要定位的字段
        submenu = tk.Menu(self.simulator_tree, tearoff=0)
        for target in targets_list:
            submenu.add_command(label=target, command=lambda t=target: self._locate_field_in_json(t))

        try:
            # 显示在鼠标位置
            submenu.tk_popup(self.simulator_tree.winfo_pointerx(), self.simulator_tree.winfo_pointery())
        finally:
            submenu.grab_release()

    def _locate_field_in_json(self, field_path: str):
        """在JSON中定位指定字段"""
        if not hasattr(self.json_editor, 'json_text') or not self.json_editor.json_text:
            messagebox.showwarning('提示', 'JSON编辑器未初始化')
            return

        json_text_widget = self.json_editor.json_text
        json_content = json_text_widget.get('1.0', tk.END)

        import re

        # 从路径中提取最后一个字段名
        # 例如：body.time -> time, body.lat -> lat, body.entries[0x01].time -> time
        parts = field_path.replace(']', '').split('.')
        if not parts:
            return

        # 获取最后一部分，可能包含[key]
        last_part = parts[-1]
        if '[' in last_part:
            field_name = last_part.split('[')[0]
        else:
            field_name = last_part

        # 搜索字段定义
        # 1. 先尝试搜索 "name": "field_name"
        name_pattern = r'"name"\s*:\s*"' + re.escape(field_name) + r'"'
        match = re.search(name_pattern, json_content)

        if match:
            # 计算位置
            pos = match.start()
            lines_before = json_content[:pos].count('\n')
            line_start = json_content.rfind('\n', 0, pos) + 1
            col = pos - line_start

            tk_pos = f"{lines_before + 1}.{col}"
            tk_end_pos = f"{lines_before + 1}.{col + len(match.group())}"

            # 定位并高亮
            json_text_widget.see(tk_pos)
            json_text_widget.tag_remove('highlight', '1.0', tk.END)
            json_text_widget.tag_add('highlight', tk_pos, tk_end_pos)
            json_text_widget.tag_config('highlight', background='yellow', foreground='black')
            json_text_widget.mark_set(tk.INSERT, tk_pos)
            json_text_widget.focus_set()

            # 2秒后取消高亮
            self.frame.after(2000, lambda: json_text_widget.tag_remove('highlight', '1.0', tk.END))
        else:
            messagebox.showwarning('提示', f'未在JSON中找到字段 "{field_name}"')

    def _add_simulator_to_json(self, simulator_data: dict):
        """将新模拟器添加到JSON的simulators数组"""
        try:
            # 获取当前JSON
            json_text = self.json_editor.json_text.get('1.0', tk.END).strip()
            config = json.loads(json_text)

            # 确保有simulators数组
            if 'simulators' not in config:
                config['simulators'] = []

            # 添加新模拟器
            config['simulators'].append(simulator_data)

            # 更新JSON
            updated_text = json.dumps(config, indent=2, ensure_ascii=False)
            self.json_editor.json_text.delete('1.0', tk.END)
            self.json_editor.json_text.insert('1.0', updated_text)

            messagebox.showinfo("成功", "模拟器已添加")
        except Exception as e:
            messagebox.showerror("错误", f"添加模拟器失败: {str(e)}")

    def _update_simulator_in_json(self, index: int, simulator_data: dict):
        """更新JSON中指定索引的模拟器"""
        try:
            # 获取当前JSON
            json_text = self.json_editor.json_text.get('1.0', tk.END).strip()
            config = json.loads(json_text)

            # 更新指定索引的模拟器
            if 'simulators' in config and 0 <= index < len(config['simulators']):
                config['simulators'][index] = simulator_data

                # 更新JSON
                updated_text = json.dumps(config, indent=2, ensure_ascii=False)
                self.json_editor.json_text.delete('1.0', tk.END)
                self.json_editor.json_text.insert('1.0', updated_text)

                messagebox.showinfo("成功", "模拟器已更新")
            else:
                messagebox.showerror("错误", "模拟器索引无效")
        except Exception as e:
            messagebox.showerror("错误", f"更新模拟器失败: {str(e)}")

    def _delete_simulator_from_json(self, index: int):
        """从JSON中删除指定索引的模拟器"""
        try:
            # 获取当前JSON
            json_text = self.json_editor.json_text.get('1.0', tk.END).strip()
            config = json.loads(json_text)

            # 删除指定索引的模拟器
            if 'simulators' in config and 0 <= index < len(config['simulators']):
                del config['simulators'][index]

                # 更新JSON
                updated_text = json.dumps(config, indent=2, ensure_ascii=False)
                self.json_editor.json_text.delete('1.0', tk.END)
                self.json_editor.json_text.insert('1.0', updated_text)

                messagebox.showinfo("成功", "模拟器已删除")
            else:
                messagebox.showerror("错误", "模拟器索引无效")
        except Exception as e:
            messagebox.showerror("错误", f"删除模拟器失败: {str(e)}")

    def _update_simulator_enable_in_json_v2(self, index: int, new_enabled: bool) -> bool:
        """更新JSON中指定索引模拟器的enable状态"""
        try:
            # 获取当前JSON
            json_text = self.json_editor.json_text.get('1.0', tk.END).strip()
            config = json.loads(json_text)

            # 更新enable状态
            if 'simulators' in config and 0 <= index < len(config['simulators']):
                config['simulators'][index]['enable'] = new_enabled

                # 更新JSON
                updated_text = json.dumps(config, indent=2, ensure_ascii=False)
                self.json_editor.json_text.delete('1.0', tk.END)
                self.json_editor.json_text.insert('1.0', updated_text)

                return True
            else:
                messagebox.showerror("错误", "模拟器索引无效")
                return False
        except Exception as e:
            messagebox.showerror("错误", f"更新模拟器状态失败: {str(e)}")
            return False
